package com.alex.statistics.method.explorationAnalysis;

import com.alex.statistics.pojo.request.explorationAnalysis.DBSCANRequest;
import com.alex.statistics.pojo.result.explorationAnalysis.DBSCANResult;
import org.springframework.stereotype.Service;
import java.util.*;
import java.util.stream.Collectors;

/**
 * DBSCAN异常检测服务
 */
@Service
public class DBSCANDetector {

    public DBSCANResult detect(DBSCANRequest request) {
        // 转换输入数据 - 关键修改：使用循环保留原始索引
        List<List<Double>> inputPoints = request.getPoints();
        List<DBSCANDataPoint> points = new ArrayList<>(inputPoints.size());

        // 循环遍历并为每个点设置正确的原始索引
        for (int i = 0; i < inputPoints.size(); i++) {
            points.add(new DBSCANDataPoint(
                    i,  // 这里设置原始索引（从0开始）
                    inputPoints.get(i).stream().mapToDouble(Double::doubleValue).toArray()
            ));
        }

        // 执行聚类（保持原有逻辑）
        DBSCANClusterer clusterer = new DBSCANClusterer(request.getEps(), request.getMinPts());
        List<List<DBSCANDataPoint>> clusters = clusterer.cluster(points);

        // 计算异常分数（保持原有逻辑）
        List<Double> scores = calculateAnomalyScores(points, clusters);

        // 构建结果（现在可以获取正确的索引）
        return new DBSCANResult(
                clusterer.getNoisePoints().stream()
                        .map(DBSCANDataPoint::getIndex)  // 获取正确的原始索引
                        .collect(Collectors.toList()),
                scores,
                clusters.size(),
                clusterer.getNoisePoints().size()
        );
    }

    // 以下方法保持不变
    private List<Double> calculateAnomalyScores(List<DBSCANDataPoint> points,
                                                List<List<DBSCANDataPoint>> clusters) {
        return points.stream()
                .map(point -> clusters.stream()
                        .mapToDouble(cluster -> calculateDistanceToCluster(point, cluster))
                        .min()
                        .orElse(Double.MAX_VALUE))
                .collect(Collectors.toList());
    }

    private double calculateDistanceToCluster(DBSCANDataPoint point, List<DBSCANDataPoint> cluster) {
        return cluster.stream()
                .mapToDouble(p -> p.distanceTo(point))
                .average()
                .orElse(Double.MAX_VALUE);
    }
}
